
// This example uses SDMMC peripheral to communicate with SD card.
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"

#define EXAMPLE_MAX_CHAR_SIZE 64


static const char *TAG = "example";

#define MOUNT_POINT "/sdcard"

static esp_err_t s_example_write_file(const char *path, char *data)
{
	ESP_LOGI(TAG, "Opening file %s", path);
	FILE *f = fopen(path, "w");
	if (f == NULL)
	{
		ESP_LOGE(TAG, "Failed to open file for writing");
		return ESP_FAIL;
	}
	fprintf(f, data);
	fclose(f);
	ESP_LOGI(TAG, "File written");

	return ESP_OK;
}

static esp_err_t s_example_read_file(const char *path)
{
	ESP_LOGI(TAG, "Reading file %s", path);
	FILE *f = fopen(path, "r");
	if (f == NULL)
	{
		ESP_LOGE(TAG, "Failed to open file for reading");
		return ESP_FAIL;
	}
	char line[EXAMPLE_MAX_CHAR_SIZE];
	fgets(line, sizeof(line), f);
	fclose(f);

	// strip newline
	char *pos = strchr(line, '\n');
	if (pos)
	{
		*pos = '\0';
	}
	ESP_LOGI(TAG, "Read from file: '%s'", line);

	return ESP_OK;
}

void app_main(void)
{
	esp_err_t ret;

	// Options for mounting the filesystem.
	// If format_if_mount_failed is set to true, SD card will be partitioned and
	// formatted in case when mounting fails.
	esp_vfs_fat_sdmmc_mount_config_t mount_config = {
		// 此选项用于开关SD卡挂载失败后是否格式化
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
			.format_if_mount_failed = true,
#else
			.format_if_mount_failed = false,
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
			.max_files = 5, 
			.allocation_unit_size = 16 * 1024};

	sdmmc_card_t *card;
	const char mount_point[] = MOUNT_POINT;
	ESP_LOGI(TAG, "Initializing SD card");

	// Use settings defined above to initialize SD card and mount FAT filesystem.
	// Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
	// Please check its source code and implement error recovery when developing
	// production applications.

	ESP_LOGI(TAG, "Using SDMMC peripheral");

	// By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
	// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC)
	// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
	sdmmc_host_t host = SDMMC_HOST_DEFAULT();

	// This initializes the slot without card detect (CD) and write protect (WP) signals.
	// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
	sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();

	// Set bus width to use:
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
	slot_config.width = 4;
#else
	slot_config.width = 1;
#endif


	// 使用ESP32的话,下面部分无用,SD卡对应的接口是唯一的,无法自定义
	// 可以参考:https://github.com/espressif/esp-idf/tree/master/examples/storage/sd_card/sdmmc#pin-assignments-for-esp32-s3
	// On chips where the GPIOs used for SD card can be configured, set them in
	// the slot_config structure:
	#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
		slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
		slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
		slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
	#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
		slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
		slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
		slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
	#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
	#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX

	// Enable internal pullups on enabled pins. The internal pullups
	// are insufficient however, please make sure 10k external pullups are
	// connected on the bus. This is for debug / example purpose only.
	slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

	ESP_LOGI(TAG, "Mounting filesystem");
	ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);

	if (ret != ESP_OK)
	{
		if (ret == ESP_FAIL)
		{
			ESP_LOGE(TAG, "Failed to mount filesystem. "
										"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
		}
		else
		{
			ESP_LOGE(TAG, "Failed to initialize the card (%s). "
										"Make sure SD card lines have pull-up resistors in place.",
							 esp_err_to_name(ret));
		}
		return;
	}
	ESP_LOGI(TAG, "Filesystem mounted");

	// Card has been initialized, print its properties
	sdmmc_card_print_info(stdout, card);

	// Use POSIX and C standard library functions to work with files:

	// First create a file.
	const char *file_hello = MOUNT_POINT "/hello.txt";
	char data[EXAMPLE_MAX_CHAR_SIZE];
	snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Hello", card->cid.name);
	ret = s_example_write_file(file_hello, data);
	if (ret != ESP_OK)
	{
		return;
	}

	const char *file_foo = MOUNT_POINT "/foo.txt";
	// Check if destination file exists before renaming
	struct stat st;
	if (stat(file_foo, &st) == 0)
	{
		// Delete it if it exists
		unlink(file_foo);
	}

	// Rename original file
	ESP_LOGI(TAG, "Renaming file %s to %s", file_hello, file_foo);
	if (rename(file_hello, file_foo) != 0)
	{
		ESP_LOGE(TAG, "Rename failed");
		return;
	}

	ret = s_example_read_file(file_foo);
	if (ret != ESP_OK)
	{
		return;
	}

	// Format FATFS
	ret = esp_vfs_fat_sdcard_format(mount_point, card);
	if (ret != ESP_OK)
	{
		ESP_LOGE(TAG, "Failed to format FATFS (%s)", esp_err_to_name(ret));
		return;
	}

	if (stat(file_foo, &st) == 0)
	{
		ESP_LOGI(TAG, "file still exists");
		return;
	}
	else
	{
		ESP_LOGI(TAG, "file doesnt exist, format done");
	}

	const char *file_nihao = MOUNT_POINT "/nihao.txt";
	memset(data, 0, EXAMPLE_MAX_CHAR_SIZE);
	snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Nihao", card->cid.name);
	ret = s_example_write_file(file_nihao, data);
	if (ret != ESP_OK)
	{
		return;
	}

	// Open file for reading
	ret = s_example_read_file(file_nihao);
	if (ret != ESP_OK)
	{
		return;
	}

	// All done, unmount partition and disable SDMMC peripheral
	esp_vfs_fat_sdcard_unmount(mount_point, card);
	ESP_LOGI(TAG, "Card unmounted");
}
